package com.corn.distribute.lock.demo.controller;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author : Jim Wu
 * @version 1.0
 * @function :
 * @since : 2021/1/15 10:40
 */

@RestController
@RequestMapping("/goods")
public class GoodsController {
    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Value("${server.port}")
    private String port;

    private final String LOCK_NAME = "MY_DISTRIBUTE_LOCK";

    @Autowired
    private RedissonClient redissonClient;

//    @GetMapping("/buy")
//    public String buyGoods() {
//        String lockValue = UUID.randomUUID().toString() + Thread.currentThread().getName();
//        try {
//            // 为了保证原子性 必须使用setIfAbsent + 超时时间(因为程序可能在执行到finally语句块之前挂掉,必须添加超时时间)
//            Boolean lockSuccess = redisTemplate.opsForValue().setIfAbsent(LOCK_NAME, lockValue, 30, TimeUnit.SECONDS);
//            if (lockSuccess) {
//                String goodsName = "goods:01";
//                Integer remindNo = Integer.valueOf(redisTemplate.opsForValue().get(goodsName).toString());
//                if (remindNo > 0) {
//                    redisTemplate.opsForValue().set(goodsName, remindNo - 1);
//                    return "卖出编号为: " + remindNo + "号商品 端口: " + port;
//                }
//            } else {
//                return "lock 失败..";
//            }
//        } finally {
//            // 为了保证 程序最终能释放锁 必须判断是自己的锁不能删别人的
////            if (null != redisTemplate.opsForValue().get(LOCK_NAME) && redisTemplate.opsForValue().get(LOCK_NAME).equals(lockValue)) {
////                redisTemplate.delete(LOCK_NAME);
////            }
//            // 使用lua脚本保证 删除的原子性 采坑建议 setResultType为Object 或者Long具体根据脚本返回值
////            DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();
////            redisScript.setResultType(Object.class);
////            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/unlock.lua")));
////            // 如果出现序列化异常 使用构造的execute方法 传入key和arg的序列化器
////            Object result = redisTemplate.execute(redisScript, Collections.singletonList(LOCK_NAME), lockValue);
////            assert result != null;
////            if (result.equals(1L)) {
////                System.out.println("删除锁成功!");
////            }
//            // 使用事务来解决 分布式锁的解锁问题
//            while (true) {
//                Serializable redisLockValue = redisTemplate.opsForValue().get(LOCK_NAME);
//                if (lockValue.equals(redisLockValue)) {
//                    // 开始watch 监控LOCK_NAME的值是不是被修改过  类似CAS 中的ABA问题
//                    redisTemplate.watch(LOCK_NAME);
//                    // 声明redisTemplate支持事务
//                    redisTemplate.setEnableTransactionSupport(true);
//                    // 开启事务
//                    redisTemplate.multi();
//                    redisTemplate.delete(LOCK_NAME);
//                    // 提交
//                    List<Object> execResult = redisTemplate.exec();
//                    if (CollectionUtils.isEmpty(execResult)) {
//                        continue;
//                    }
//                    // 解锁watch
//                    redisTemplate.unwatch();
//                }
//                break;
//            }
//
//        }
//        return "商品已经售完!!!";
//    }

    @GetMapping("/buy")
    public String buyGoods() {
        RLock lock = redissonClient.getLock(LOCK_NAME);
        lock.lock();
        try {
            String goodsName = "goods:01";
            Integer remindNo = Integer.valueOf(redisTemplate.opsForValue().get(goodsName).toString());
            if (remindNo > 0) {
                redisTemplate.opsForValue().set(goodsName, remindNo - 1);
                return "卖出编号为: " + remindNo + "号商品 端口: " + port;
            }
            return "商品已经售完!!!";
        } finally {
            // 保证最后锁释放不会抛出异常需要判断 是否锁定和是否是当前线程锁定
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}